/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */
#include "TCPSocket.h"

namespace PQNet
{
	TCPSocket::TCPSocket() : _hostAddr(""),
		_opened(false),_socket(INVALID_SOCKET),_port(0)
	{

		memset(&_socketAddr, 0, sizeof(SOCKADDR_IN));

		_port = ntohs(_socketAddr.sin_port );
		_hostAddr=inet_ntoa(_socketAddr.sin_addr);
	}

	TCPSocket::TCPSocket(SOCKET socket) : _hostAddr(""),
		_opened(false),_socket(socket),_port(0)
	{

		memset(&_socketAddr, 0, sizeof(SOCKADDR_IN));

		_port = ntohs(_socketAddr.sin_port );
		_hostAddr=inet_ntoa(_socketAddr.sin_addr);

	}


	TCPSocket::TCPSocket(std::string host,uint32 port) : _hostAddr(host), 
		_opened(false),_socket(INVALID_SOCKET),_port(0)
	{
		open();
	}

	TCPSocket::~TCPSocket()
	{
		close();
	}

	bool TCPSocket::open()
	{
		if(_opened){
			return true;//already opened
		}

		//open(create) socket
		_socket = socket(AF_INET,SOCK_STREAM,0);
		memset(&_socketAddr , 0 , sizeof(_socketAddr));
		_socketAddr.sin_family = AF_INET;

		_opened=true;
		if(isValid()){
			return true;
		}else{
			return false;
		}
	}

	void TCPSocket::close()
	{
		if(!_opened){
			return;
		}

		if(isValid()&&!isSocketError()){
			if ( closesocket(_socket) == SOCKET_ERROR ){
				//INT lastError = WSAGetLastError() ;
			}
		}
		_socket=INVALID_SOCKET;
		memset( &_socketAddr, 0, sizeof(SOCKADDR_IN) ) ;
		_hostAddr="";
		_port=0;

		_opened=false;//closed
	}

	bool TCPSocket::isSocketError() const
	{
		INT error;
		UINT len = sizeof(error);
		if ( getsockopt(_socket , SOL_SOCKET , SO_ERROR , (CHAR*)&error , (int*)&len ) == SOCKET_ERROR ){
			//INT lastError = WSAGetLastError() ;
			return true;
		}
		return false;
	}

	bool TCPSocket::connect(std::string host, uint32 port)
	{
		_hostAddr=host;
		_port=port;
		return connect();

	}

	bool TCPSocket::connect()
	{
		_socketAddr.sin_addr.s_addr = inet_addr(_hostAddr.c_str());
		_socketAddr.sin_port=htons(_port);
	
		if (::connect(_socket,(const struct sockaddr *)&_socketAddr,sizeof(_socketAddr)) == SOCKET_ERROR ) {
			return false;
		}else{
			return true;
		}

	}

	bool TCPSocket::setReuseAddr(bool on)
	{
		int opt = ((on==true) ? 1 : 0);
		
		if(::setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, (CHAR*)opt, sizeof(opt)) == SOCKET_ERROR){
			return false;
		}

		return true;
	}


	bool TCPSocket::bind(int port)
	{
		_port=port;
		_socketAddr.sin_addr.s_addr=htonl(INADDR_ANY);
		_socketAddr.sin_port=htons(_port);

		if (::bind (_socket,(const struct sockaddr*)&_socketAddr,sizeof(_socketAddr)) == SOCKET_ERROR){
			return false;
		}
		return true;
	}


	bool TCPSocket::listen(int backlog)
	{
		if (::listen(_socket , backlog ) == SOCKET_ERROR ){
			return false;
		}
		return true;
	}


	bool TCPSocket::setNonBlocking (bool on ) 
	{ 
		ULONG arg= ((on == true) ? 1 : 0);
		if(ioctlsocket(_socket,FIONBIO,&arg)==SOCKET_ERROR){
			return false;
		}

		return true;
	}

	SOCKET TCPSocket::accept(struct sockaddr* addr, int* addrlen)
	{
		SOCKET client = ::accept(_socket , addr , (int*)addrlen );
		return client;
	}


	int TCPSocket::getLinger() const
	{
		struct linger ling;
		int len = sizeof(ling);

		if(getsockopt(_socket,SOL_SOCKET,SO_LINGER,(char*)&ling,&len)==SOCKET_ERROR){
			return 0;//error
		}

		return ling.l_linger;
	}

	bool TCPSocket::setLinger(int time)
	{
		struct linger ling;
		ling.l_onoff = time > 0 ? 1 : 0;
		ling.l_linger = time;

		if(::setsockopt(_socket,SOL_SOCKET,SO_LINGER,(const char*)&ling,sizeof(ling))==SOCKET_ERROR){
			return false;
		}

		return true;
		
	}


	int TCPSocket::receive(void* buf, int len, int flags)
	{
		int nrecv = ::recv(_socket,(char*)buf,len,flags);
		return nrecv;
	}


	int TCPSocket::available() const
	{
		ulong arg = 0;
		if(::ioctlsocket(_socket,FIONREAD,&arg) == SOCKET_ERROR){
			return arg;
		}

		return arg;
	}

}
